/*
 * clog_spec.c
 *
 *  Created on: 2012-8-23
 *      Author: lin
 */

#include "zmalloc/zmalloc.h"
#include "clog_spec.h"
#include "util/u_buf.h"
#include "clog_thread_local.h"
#include "clog_level.h"

#define MAXLEN_CFG_LINE (1024 * 4)
#define CLOG_DEFAULT_TIME_FMT "%F %T"


static inline int _write_op(const clog_spec_t *a_spec, u_buf_t *buf,
        clog_thread_local_t *local) {
    clog_event_t *event = local->event;
    clog_level_t *level = event->level;
    switch (a_spec->write_op) {
    case 1:
        //write str
        return u_buf_append(buf, a_spec->str, a_spec->strlen);
    case 2:
        //write category
        return u_buf_append(buf, event->category_name, event->category_name_len);
    case 3:
        //write file
        return u_buf_append(buf, event->file_name, event->file_name_len);
    case 4:
        //write file neat
        char *p;
        if ((p = strrchr(event->file_name, '/')) != NULL) {
            return u_buf_append(buf, p + 1,
                    (char*) event->file_name + event->file_name_len - p - 1);
        } else {
            return u_buf_append(buf, event->file_name, event->file_name_len);
        }
    case 5:
        //write src line
        return u_buf_printf_dec64(buf, event->line, 0);
    case 6:
        //write src fun
        return u_buf_append(buf, event->func, event->func_len);
    case 7:
        //write newline
        return u_buf_append(buf, STR_NEWLINE, STR_NEWLINE_LEN);
    case 8:
        return u_buf_append(buf, "%", 1);
    case 9:
        //write level lowcase
        return u_buf_append(buf, clog_level_name_lowcase(level), level->strlen);
    case 10:
        //write level upcase
        return u_buf_append(buf, clog_level_name_upcase(level), level->strlen);
    case 11:
        //write umsg
        if (event->str_format) {
            return u_buf_vprintf(buf, event->str_format, event->str_args);
        } else {
            return u_buf_append(buf, "format=(null)",
                    sizeof("format=(null)") - 1);
        }
    default:
        assert(0);
        break;
    }
}
clog_spec_t *clog_spec_new(const char *pattern_start, char **pattern_next) {
    char *p = pattern_start;
    int nscan = 0;
    int nread = 0;
    char print_fmt[MAXLEN_CFG_LINE];
    print_fmt[0] = '\0';

    switch (*p) {
    case '%':
        /* a string begin with %: %12.35d(%F %X,%l) */

        /* process width and precision char in %-12.35P */
        nscan = sscanf(p, "%%%[.0-9-]%n", print_fmt, &nread);
        if (nscan == 1) {
            a_spec->gen_msg = zlog_spec_gen_msg_reformat;
            a_spec->gen_path = zlog_spec_gen_path_reformat;
            a_spec->gen_archive_path = zlog_spec_gen_archive_path_reformat;
            if (zlog_spec_parse_print_fmt(a_spec)) {
                zc_error("zlog_spec_parse_print_fmt fail");
                goto err;
            }
        } else {
            nread = 1; /* skip the % char */
            a_spec->gen_msg = zlog_spec_gen_msg_direct;
            a_spec->gen_path = zlog_spec_gen_path_direct;
            a_spec->gen_archive_path = zlog_spec_gen_archive_path_direct;
        }

        p += nread;

        if (*p == 'd') {
            if (*(p + 1) != '(') {
                /* without '(' , use default */
                strcpy(a_spec->time_fmt, ZLOG_DEFAULT_TIME_FMT);
                p++;
            } else if (STRNCMP(p, ==, "d()", 3)) {
                /* with () but without detail time format,
                 * keep a_spec->time_fmt=="" */
                strcpy(a_spec->time_fmt, ZLOG_DEFAULT_TIME_FMT);
                p += 3;
            } else {
                nscan = sscanf(p, "d(%[^)])%n", a_spec->time_fmt, &nread);
                if (nscan != 1) {
                    nread = 0;
                }
                p += nread;
                if (*(p - 1) != ')') {
                    zc_error("in string[%s] can't find match \')\'",
                            a_spec->str);
                    goto err;
                }
            }

            *pattern_next = p;
            a_spec->len = p - a_spec->str;
            a_spec->write_buf = zlog_spec_write_time;
            break;
        }

        if (*p == 'M') {
            nscan = sscanf(p, "M(%[^)])%n", a_spec->mdc_key, &nread);
            if (nscan != 1) {
                nread = 0;
                if (STRNCMP(p, ==, "M()", 3)) {
                    nread = 3;
                }
            }
            p += nread;
            if (*(p - 1) != ')') {
                zc_error("in string[%s] can't find match \')\'", a_spec->str);
                goto err;
            }

            *pattern_next = p;
            a_spec->len = p - a_spec->str;
            a_spec->write_buf = zlog_spec_write_mdc;
            break;
        }

        if (STRNCMP(p, ==, "ms", 2)) {
            p += 2;
            *pattern_next = p;
            a_spec->len = p - a_spec->str;
            a_spec->write_buf = zlog_spec_write_ms;
            break;
        } else if (STRNCMP(p, ==, "us", 2)) {
            p += 2;
            *pattern_next = p;
            a_spec->len = p - a_spec->str;
            a_spec->write_buf = zlog_spec_write_us;
            break;
        }

        *pattern_next = p + 1;
        a_spec->len = p - a_spec->str + 1;

        switch (*p) {
        case 'c':
            a_spec->write_buf = zlog_spec_write_category;
            break;
        case 'D':
            a_spec->write_buf = zlog_spec_write_time_D;
            break;
        case 'F':
            a_spec->write_buf = zlog_spec_write_srcfile;
            break;
        case 'f':
            a_spec->write_buf = zlog_spec_write_srcfile_neat;
            break;
        case 'H':
            a_spec->write_buf = zlog_spec_write_hostname;
            break;
        case 'L':
            a_spec->write_buf = zlog_spec_write_srcline;
            break;
        case 'm':
            a_spec->write_buf = zlog_spec_write_usrmsg;
            break;
        case 'n':
            a_spec->write_buf = zlog_spec_write_newline;
            break;
        case 'p':
            a_spec->write_buf = zlog_spec_write_pid;
            break;
        case 'U':
            a_spec->write_buf = zlog_spec_write_srcfunc;
            break;
        case 'v':
            a_spec->write_buf = zlog_spec_write_level_lowercase;
            break;
        case 'V':
            a_spec->write_buf = zlog_spec_write_level_uppercase;
            break;
        case 't':
            a_spec->write_buf = zlog_spec_write_tid_hex;
            break;
        case 'T':
            a_spec->write_buf = zlog_spec_write_tid_long;
            break;
        case '%':
            a_spec->write_buf = zlog_spec_write_percent;
            break;
        default:
            zc_error("str[%s] in wrong format, p[%c]", a_spec->str, *p);
            goto err;
        }
        break;
    default:
        /* a const string: /home/bb */
        *pattern_next = strchr(p, '%');
        if (*pattern_next) {
            a_spec->len = *pattern_next - p;
        } else {
            a_spec->len = strlen(p);
            *pattern_next = p + a_spec->len;
        }
        a_spec->write_buf = zlog_spec_write_str;
        a_spec->gen_msg = zlog_spec_gen_msg_direct;
        a_spec->gen_path = zlog_spec_gen_path_direct;
        a_spec->gen_archive_path = zlog_spec_gen_archive_path_direct;
    }
    return NULL;
    err: return NULL;

}
void clog_spec_free(clog_spec_t *spec);
